#include "touch_tracker.h"

#if USE_TOUCH_TRACKER
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include "../../board.h"
#include "../common/lv_pm.h"
#include "lvgl/src/lv_core/lv_group.h"
#include "lvgl/src/lv_misc/lv_tsrb.h"
#include "crane_onkey.h"

#define TT_ERR(fmt, ...) printf("touch_tracker/%s(%d) E:" fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__)
#define TT_DBG(fmt, ...) printf("touch_tracker/%s(%d) D:" fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__)
#define TT_INF(fmt, ...) printf("touch_tracker/%s(%d) I:" fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__)

#define TP_DATA_BUF_SIZE (256) /* must be power of 2 */
#define HOR_RES LV_HOR_RES
#define VER_RES LV_VER_RES
#define OS_CYCLE_PER_MS 1
#define VK_X 120
#define VK_Y 300

static int vk_state = LV_INDEV_STATE_REL;
static char shell_tp_data_buf[TP_DATA_BUF_SIZE];
static tsrb_t shell_tp_rb;
static int shell_state_last;
static int shell_x_last, shell_y_last;
#if TT_USE_OSA_HISR
static void *tt_hisr_ref;
#endif

static touch_tracker_dev_info priv_dev_info;

extern bool watch_get_lcd_status(void);
extern void onkey_wakeup(void);
extern void lv_watch_go_home(void);
extern uint8_t setting_get_backlight_timeout(void);
extern void UOS_SleepMs(UINT32 ms);

static int touch_tracker_add_point_state(touch_tracker_data *state, tsrb_t *tsrb)
{
    unsigned x, y;

    if (tsrb_full(tsrb)) {
        TT_ERR("tsrb full!!!\r\n");
        return -1;
    }

    x = state->x;
    y = state->y;

    if (x > (HOR_RES - 1) || y > VK_Y) {
        TT_ERR("bad (%u, %u)\r\n", x, y);
        return -1;
    }

    if ((TP_DATA_BUF_SIZE - tsrb_avail(tsrb)) < sizeof(touch_tracker_data)) {
        TT_ERR("%d, %d\r\n", x, y);
        return -1;
    }
    else {
        /*TT_DBG("D: %d %d %d %d\n", state->x, state->y, state->width, state->sub_status); */
        tsrb_add(tsrb, (const char *)state, sizeof(touch_tracker_data));
    }

    return 0;
}

static int touch_tracker_get_point_state(lv_indev_data_t *msg, tsrb_t *tsrb)
{
    touch_tracker_data point_coord;

    if (tsrb_empty(tsrb)) {
        msg->point.x = shell_x_last;
        msg->point.y = shell_y_last;
        msg->state = shell_state_last;

        /*TT_DBG("%s(%d): %d %d %d\n", __FUNCTION__, __LINE__, msg->x, msg->y, msg->event); */

        return 1;
    }
    else {
        tsrb_get(tsrb, (char *)&point_coord, sizeof(touch_tracker_data));
        msg->point.x = point_coord.x;
        msg->point.y = point_coord.y;

        if (point_coord.sub_status == TOUCH_TRACKER_DOWN) {
            msg->state = LV_INDEV_STATE_PR;
        }
        else {
            msg->state = LV_INDEV_STATE_REL;
        }

        shell_x_last = msg->point.x;
        shell_y_last = msg->point.y;
        shell_state_last = msg->state;
        /*TT_DBG("%s(%d): %d %d %d\n", __FUNCTION__, __LINE__, msg->x, msg->y, msg->event); */

        if (tsrb_empty(tsrb)) {
            return 0;
        }

        return 0;
    }
}

void touch_tracker_reset(void)
{
    tsrb_init(&shell_tp_rb, shell_tp_data_buf, TP_DATA_BUF_SIZE);
    shell_state_last = 0;
}

void touch_tracker_Suspend(void *arg)
{
    TT_DBG("enter\r\n");
}

void touch_tracker_Resume(void *arg)
{
    TT_DBG("enter\r\n");

    touch_tracker_reset();
}

bool touch_tracker_read(lv_indev_drv_t * indev_drv, lv_indev_data_t *data)
{
    (void) indev_drv;      /*Unused*/

    touch_tracker_get_point_state(data, &shell_tp_rb);
    if ((data->point.x == VK_X) && (data->point.y == VK_Y)) {
            vk_state = data->state;
    }

    return false; /* no more data to read */
}

bool touch_tracker_vk_read(lv_indev_data_t *data)
{
    data->key = LV_KEY_ESC;
    data->state = vk_state;
    // printf("vk: %s\n", (data->state == LV_INDEV_STATE_REL)?"release":"press");
    return false; /* no more data to read */
}

void touch_tracker_set_data(void *data)
{
    touch_tracker_data *tt_data = (touch_tracker_data *)data;
    TT_DBG("x:%d, y:%d, width:%d, sub_status:%d\r\n", tt_data->x, tt_data->y, tt_data->width, tt_data->sub_status);

    if (memcmp(&priv_dev_info.data, data, sizeof(touch_tracker_data))) {
        memcpy(&priv_dev_info.data, data, sizeof(touch_tracker_data));
#if TT_USE_OSA_HISR
        OS_Activate_HISR(&tt_hisr_ref);
#else
        touch_tracker_add_point_state(&priv_dev_info.data, &shell_tp_rb);
#endif
    }
    else {
        TT_DBG("same data.\r\n");
    }
}

touch_tracker_data touch_tracker_get_data(void)
{
    /*TT_DBG("%s(%d) x:%d, y:%d, width:%d, sub_status:%d\n", __FUNCTION__, __LINE__, */
    /*       priv_dev_info.data.x, priv_dev_info.data.y, priv_dev_info.data.width, priv_dev_info.data.sub_status); */

    return priv_dev_info.data;
}

#if TT_USE_OSA_HISR
static void tt_hisr(void)
{
    touch_tracker_add_point_state(&priv_dev_info.data, &shell_tp_rb);
}
#endif

/************************************************************************************
 *
 *                           tt play implementation
 *
 ************************************************************************************/
#define PLAYBACK_BUF_LEN_MAX    64
static TASK_HANDLE *s_tt_play_task_handle = NULL;
static char s_tt_play_file[128];
static uint8_t s_tt_play_state;           /*0 - Ready. 1 - Ongoing. 2 - Finish. 3 - Stop. */

static int _decstr2int(const char *str)
{
    int num = 0;
    char *d_p = (char *)str;

    for (; *d_p != '\0'; d_p++) {
        if ((*d_p >= '0') && (*d_p <= '9')) {
            num = (10 * num) + (*d_p - '0');
        }
    }
    return num;
}

static void tt_go_home_task(lv_task_t * task)
{
    lv_watch_go_home();

    lv_task_del(task);
    task = NULL;
}

int touch_tracker_wakeup_to_home_page(void)
{
    TT_DBG("wakeup to home page.\r\n");
    if (!watch_get_lcd_status()) {
        onkey_wakeup();
        UOS_SleepMs(1000);
    }

    lv_task_t * tt_go_home_task_handle = NULL;
    tt_go_home_task_handle = lv_task_create(tt_go_home_task, 100, LV_TASK_PRIO_HIGH, NULL);
    lv_task_once(tt_go_home_task_handle);
    lv_task_ready(tt_go_home_task_handle);

    UOS_SleepMs(1000);

    return 1;
}

static void touch_tracker_play_task(void * para)
{
    lv_fs_file_t file = {0};
    char buf[PLAYBACK_BUF_LEN_MAX] = { 0 };
    char cmd[PLAYBACK_BUF_LEN_MAX] = { 0 };
    char val[PLAYBACK_BUF_LEN_MAX] = { 0 };
    char ch = 0;
    int n = 0;
    uint32_t size = 0;

    lv_fs_res_t res = lv_fs_open(&file, s_tt_play_file, LV_FS_MODE_RD);
    if (res != LV_FS_RES_OK) {
        TT_ERR("Read %s failed.\r\n", s_tt_play_file);
        s_tt_play_state = 0;
    }
    else {
        TT_DBG("Read %s begin.\r\n", s_tt_play_file);
        s_tt_play_state = 1;
        while (1) {
            if (s_tt_play_state == 3) {
                TT_DBG("Read %s stop.\r\n", s_tt_play_file);
                break;
            }
            lv_fs_read(&file, &ch, sizeof(char), &size);
            /*TT_DBG("ch:0x%x, buf:%s\n", ch, buf); */
            if (size == sizeof(char)) {
                if (ch == '\r' || ch == '\n') {
                    continue;
                }
                else if (ch == ':') {
                    memset(cmd, 0, PLAYBACK_BUF_LEN_MAX);
                    strcpy(cmd, buf);
                    memset(buf, 0, PLAYBACK_BUF_LEN_MAX);
                    n = 0;
                }
                else if (ch == '.') {
                    memset(val, 0, PLAYBACK_BUF_LEN_MAX);
                    strcpy(val, buf);
                    memset(buf, 0, PLAYBACK_BUF_LEN_MAX);
                    n = 0;

                    TT_DBG("point:%s, val:%s.\r\n", cmd, val);
                    if (strcmp(cmd, "point") == 0) {
                        if (!watch_get_lcd_status()) {
                            onkey_wakeup();
                            UOS_SleepMs(2000);
                        }

                        sscanf(val, "%d,%d,%d,%d", (int *)&priv_dev_info.data.x, (int *)&priv_dev_info.data.y, (int *)&priv_dev_info.data.width, (int *)&priv_dev_info.data.sub_status);
#if TT_USE_OSA_HISR
                        OS_Activate_HISR(&tt_hisr_ref);
#else
                        touch_tracker_add_point_state(&priv_dev_info.data, &shell_tp_rb);
#endif
                    }
                    else if (strcmp(cmd, "delay") == 0) {
                        uint32_t delay = _decstr2int(val);
                        UOS_SleepMs(delay);
                    }
                }
                else {
                    buf[n] = ch;
                    n++;

                    if (n >= PLAYBACK_BUF_LEN_MAX) {
                        TT_ERR("There is a invalid cmd in %s.\r\n", s_tt_play_file);
                    }
                }
            }
            else {
                TT_DBG("Read %s finish.\r\n", s_tt_play_file);
                s_tt_play_state = 2;
                break;
            }
        }
        lv_fs_close(&file);
    }

    uos_delete_task(s_tt_play_task_handle);
    s_tt_play_task_handle = NULL;
    touch_tracker_reset();
}

void touch_tracker_play_on(char *file)
{
    if (touch_tracker_is_playing()) {
        TT_ERR("tt play is already running!\r\n");
        return;
    }

    if (s_tt_play_task_handle) {
        uos_delete_task(s_tt_play_task_handle);
        s_tt_play_task_handle = NULL;
    }

    if (s_tt_play_task_handle == NULL) {
        if (strlen(file) <= 3) { //C:/
            TT_ERR("The length of the config file name is invalid.\r\n");
            return;
        } else if (strlen(file) > (sizeof(s_tt_play_file) - 1)) {
            TT_ERR("The length of the config file name is over range, the max length is %d.\r\n", sizeof(s_tt_play_file) - 1);
            return;
        }

        memset(s_tt_play_file, 0, sizeof(s_tt_play_file));
        memcpy(s_tt_play_file, file, strlen(file));
        s_tt_play_state = 1;
        tsrb_init(&shell_tp_rb, shell_tp_data_buf, TP_DATA_BUF_SIZE);
        touch_tracker_wakeup_to_home_page();

        s_tt_play_task_handle = uos_create_task(touch_tracker_play_task, NULL, 0, 0x1000, 240, "tt_play_task");
    }
}

void touch_tracker_play_off(void)
{
    s_tt_play_state = 3;
}

char *touch_tracker_get_play_file(void)
{
    if (touch_tracker_is_playing()) {
        return s_tt_play_file;
    }

    return NULL;
}

bool touch_tracker_is_playing(void)
{
    return s_tt_play_state == 1;
}

/************************************************************************************
 *
 *                           tt record implementation
 *
 ************************************************************************************/
#define TT_RECORD_FILE_PREFIX       "D:/tt_"
#define TT_RECORD_FILE_MAX_NUM      10
#define TT_RECORD_FILENAME_MAX_NUM  32
#define TT_RECORD_BUFF_MAX          256
#define TT_RECORD_SECURECRT_HEADER  "\
#$language = \"Python\"\r\n\
#$interface = \"1.0\"\r\n\
def wakeup_to_home_page():\r\n\
	crt.Screen.Send('%s\\r\\n')\r\n\
	crt.Sleep(3000)\r\n\r\n\
def Main():\r\n\
	wakeup_to_home_page()\r\n"

#define touch_tracker_snprintf(s, n, fmt, ...) snprintf(s, n, fmt, ## __VA_ARGS__)
#define TOUCH_TRACKER_MSGQ_MAX 8

static bool s_is_tt_recording = 0;
static lv_fs_file_t s_tt_record_fd = {0};
static char s_tt_record_file[128] = {0};
static uint8_t s_tt_mode = 0;
static uint32_t s_tt_cmd_time_last = 0;
static uint8_t s_tt_shell = 0;

static int touch_tracker_write_record_file(touch_tracker_data *point);

void touch_tracker_record(void *data)
{
    if (!touch_tracker_is_recording()) {
        if (s_tt_cmd_time_last != 0) {
            s_tt_cmd_time_last = 0;
        }
        return;
    }

    touch_tracker_write_record_file((touch_tracker_data *)data);
}

static int touch_tracker_write_record_file(touch_tracker_data *point)
{
    uint32_t size = 0;
    uint32_t rn = 0;
    char record_buf[TT_RECORD_BUFF_MAX] = { 0 };

    if (s_tt_record_fd.file_d == NULL || s_tt_record_fd.drv == NULL) {
        s_is_tt_recording = FALSE;
        TT_ERR("%s: please check tt record file: %s\r\n", __FUNCTION__, s_tt_record_file);
        return -1;
    }

    if (s_tt_cmd_time_last != 0) {
        uint32_t elapseTime = (uint32_t)(lv_tick_get() / OS_CYCLE_PER_MS) - s_tt_cmd_time_last;
        if(s_tt_mode == 1) { // SecureCRT script
            if(elapseTime/1000 > setting_get_backlight_timeout()) {
                size = touch_tracker_snprintf(record_buf + size, TT_RECORD_BUFF_MAX, "	crt.Sleep(%d)\r\n", elapseTime);
                if(s_tt_shell) {
                    size += touch_tracker_snprintf(record_buf + size, (TT_RECORD_BUFF_MAX - size), "	crt.Screen.Send('tt onkey\\r\\n')\r\ncrt.	Sleep(3000)\r\n");
                } else {
                    size += touch_tracker_snprintf(record_buf + size, (TT_RECORD_BUFF_MAX - size), "	crt.Screen.Send('AT*TT=\"onkey\"\\r\\n')\r\ncrt.	Sleep(3000)\r\n");
                }
            } else {
                size = touch_tracker_snprintf(record_buf + size, TT_RECORD_BUFF_MAX, "	crt.Sleep(%d)\r\n", (elapseTime < 20 ? 20 :elapseTime));
            }
        } else {
            size = touch_tracker_snprintf(record_buf, TT_RECORD_BUFF_MAX, "delay:%d.\r\n", elapseTime);
        }
    }
    s_tt_cmd_time_last = (uint32_t)(lv_tick_get() / OS_CYCLE_PER_MS);

    if(s_tt_mode == 1) { // SecureCRT script
        uint8_t state = (point->sub_status & 0x7) ? 0 : 1;
        if(s_tt_shell) {
            size += touch_tracker_snprintf(record_buf + size, (TT_RECORD_BUFF_MAX - size), "	crt.Screen.Send('tt point %d %d %d %d\\r\\n')\r\n", point->x, point->y, point->width, state);
            if(state) {
                size += touch_tracker_snprintf(record_buf + size, (TT_RECORD_BUFF_MAX - size), "	crt.Sleep(20)\r\n	crt.Screen.Send('\\r\\n')\r\n");
            }
        } else {
            size += touch_tracker_snprintf(record_buf + size, (TT_RECORD_BUFF_MAX - size), "	crt.Screen.Send('AT*TT=\"point\",\"set\",%d,%d,%d,%d\\r\\n')\r\n", point->x, point->y, point->width, state);
        }
    } else {
        size += touch_tracker_snprintf(record_buf + size, (TT_RECORD_BUFF_MAX - size), "point:%d,%d,%d,%d.\r\n", point->x, point->y, point->width, (point->sub_status & 0x7) ? 0 : 1);
    }
    lv_fs_write(&s_tt_record_fd, (void *)record_buf, size, &rn);

    return 0;
}

void touch_tracker_record_on(uint8_t isShell)
{
    lv_fs_file_t file = {0};
    lv_fs_res_t res = LV_FS_RES_OK;
    int i = 0;
    uint32_t size = 0;
    uint32_t rn = 0;
    char record_buf[TT_RECORD_BUFF_MAX*4] = { 0 };

    if (touch_tracker_is_recording()) {
        TT_ERR("tt record is already running!\r\n");
        return;
    }

    memset(s_tt_record_file, 0x0, sizeof(s_tt_record_file));

    for (i = 0; i < TT_RECORD_FILE_MAX_NUM; i++) {
        if(s_tt_mode == 1) { // SecureCRT script
            if(isShell) {
                touch_tracker_snprintf(s_tt_record_file, sizeof(s_tt_record_file), "%s%d_sh.py", TT_RECORD_FILE_PREFIX, i);
            } else {
                touch_tracker_snprintf(s_tt_record_file, sizeof(s_tt_record_file), "%s%d_at.py", TT_RECORD_FILE_PREFIX, i);
            }
        } else {
            touch_tracker_snprintf(s_tt_record_file, sizeof(s_tt_record_file), "%s%d", TT_RECORD_FILE_PREFIX, i);
        }

        res = lv_fs_open(&file, s_tt_record_file, LV_FS_MODE_RD);
        if(res != LV_FS_RES_OK) { /*found a useful index */
            break;
        }
        lv_fs_close(&file);
    }

    if (i == TT_RECORD_FILE_MAX_NUM) { /*index is full */
        s_is_tt_recording = FALSE;
        TT_ERR("tt record file is full, please remove some to start record again.\r\n");
        return;
    }

	res = lv_fs_open(&s_tt_record_fd, s_tt_record_file, LV_FS_MODE_WR);
    if(res != LV_FS_RES_OK) {
        s_is_tt_recording = FALSE;
        TT_ERR("open file %s fail errno:%d.\r\n", s_tt_record_file, errno);
        return;
    }

    s_tt_shell = isShell;

    if(s_tt_mode == 1) { // SecureCRT script
        if(s_tt_shell) {
            size = touch_tracker_snprintf(record_buf, sizeof(record_buf), TT_RECORD_SECURECRT_HEADER, "tt app home");
        } else {
            size = touch_tracker_snprintf(record_buf, sizeof(record_buf), TT_RECORD_SECURECRT_HEADER, "AT*TT=\"app\",\"home\"");
        }
        lv_fs_write(&s_tt_record_fd, (void *)record_buf, size, &rn);
    }

    s_is_tt_recording = TRUE;
    if (touch_tracker_wakeup_to_home_page()) {
        s_tt_cmd_time_last = 0;
    }
    else {
        TT_DBG("touch_tracker_wakeup_to_home_page failed!\r\n");
        lv_fs_close(&s_tt_record_fd);
        s_is_tt_recording = FALSE;
    }
}

void touch_tracker_record_off(void)
{
    uint32_t size = 0;
    uint32_t rn = 0;
    char record_buf[TT_RECORD_BUFF_MAX] = { 0 };

    if (touch_tracker_is_recording()) {
        if(s_tt_mode == 1) { // SecureCRT script
            if(s_tt_shell) {
                size = touch_tracker_snprintf(record_buf, TT_RECORD_BUFF_MAX, "	crt.Sleep(20)\r\n	crt.Screen.Send('tt off\\r\\n')\r\n	crt.Sleep(20)\r\n	crt.Screen.Send('\\r\\n')\r\n");
            } else {
                size = touch_tracker_snprintf(record_buf, TT_RECORD_BUFF_MAX, "	crt.Sleep(20)\r\n	crt.Screen.Send('AT*TT=\"off\"\\r\\n')\r\n");
            }
            size += touch_tracker_snprintf(record_buf + size, (TT_RECORD_BUFF_MAX - size), "\r\nMain()");
            lv_fs_write(&s_tt_record_fd, (void *)record_buf, size, &rn);
        }

        s_is_tt_recording = FALSE;
        s_tt_cmd_time_last = 0;
        lv_fs_close(&s_tt_record_fd);
    }
    else {
        TT_ERR("tt record is not running!\r\n");
        return;
    }
}

void touch_tracker_get_record_file(char *file)
{
    if(file) {
        strcpy(file, s_tt_record_file);
    }
}

bool touch_tracker_is_recording(void)
{
    return s_is_tt_recording;
}

uint8_t touch_tracker_get_mode(void)
{
    return s_tt_mode;
}

void touch_tracker_set_mode(uint8_t mode)
{
    s_tt_mode = mode;
    memset(s_tt_record_file, 0x0, sizeof(s_tt_record_file));
}

void touch_tracker_app(char *app)
{
    if(app == NULL) {
        return;
    }

    if (strcmp(app, "home") == 0) {
        touch_tracker_wakeup_to_home_page();
    }
}

static tt_func_list g_tt_func_list = {
    .tt_is_playing = touch_tracker_is_playing,
    .tt_is_recording = touch_tracker_is_recording,
    .tt_record_on = touch_tracker_record_on,
    .tt_record_off = touch_tracker_record_off,
    .tt_play_on = touch_tracker_play_on,
    .tt_play_off = touch_tracker_play_off,
    .tt_get_mode = touch_tracker_get_mode,
    .tt_get_record_file = touch_tracker_get_record_file,
    .tt_set_data = touch_tracker_set_data,
    .tt_onkey = touch_tracker_onkey,
    .tt_set_mode = touch_tracker_set_mode,
    .tt_app = touch_tracker_app,
    .tt_reset = touch_tracker_reset,
};

int touch_tracker_init(void)
{
    TT_DBG("enter\r\n");

    memset(&priv_dev_info, 0, sizeof(touch_tracker_dev_info));
    tsrb_init(&shell_tp_rb, shell_tp_data_buf, TP_DATA_BUF_SIZE);

#if TT_USE_OSA_HISR
    OS_Create_HISR(&tt_hisr_ref, "tt_hisr", tt_hisr, HISR_PRIORITY_2);
#endif

    TT_CMD_CALLBACK_REGISTER(&g_tt_func_list);

    return 0;
}

#endif /* USE_TOUCH_TRACKER */
